package StoreConnector;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.UUID;
import java.util.Vector;

import net.jini.core.entry.Entry;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.UnknownEventException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.core.transaction.TransactionFactory;
import net.jini.core.transaction.Transaction.Created;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.lookup.entry.Name;
import net.jini.space.JavaSpace;

import be.ac.ua.codebase.BankInterface;
import be.ac.ua.codebase.CustomerInterface;
import be.ac.ua.codebase.Product;
import be.ac.ua.codebase.ProductOffer;
import be.ac.ua.codebase.StoreConnectorInterface;
import be.ac.ua.codebase.StoreInterface;

public class StoreConnectorImplementation implements StoreConnectorInterface, DiscoveryListener, ServiceDiscoveryListener{
	private Vector<ServiceRegistrar> fRegistrars = new Vector<ServiceRegistrar>();
	private Vector<StoreInterface> fStores = new Vector<StoreInterface>();

	public StoreConnectorImplementation() throws IOException {
		String[] groups = new String[] {"NDFLEXTRADE"};
        LookupDiscoveryManager lookupDiscovery = new LookupDiscoveryManager(groups, null, this);
        ServiceDiscoveryManager serviceDiscovery = new ServiceDiscoveryManager(lookupDiscovery, new LeaseRenewalManager());
		Class[] classes = new Class[] {StoreInterface.class};
        LookupCache cache = serviceDiscovery.createLookupCache(new ServiceTemplate(null, classes, null), null, this);
	}
	
	private StoreInterface getStore() {
		// Round robin
		fStores.add(fStores.elementAt(0));
		fStores.remove(0);
		return fStores.elementAt(0);
	}
	
	public void addProduct(Product product) throws RemoteException, TransactionException, LeaseDeniedException {
		this.getStore().addProduct(product);
		
		//Get JavaSpace and Transaction Manager
		JavaSpace space = this.getSpace();
		TransactionManager transManager = this.getTransactionManager();
		
		//Initialize transaction
		Created created = TransactionFactory.create(transManager, 5000);
		Transaction trans = created.transaction;
		
		//Create and write entry
		ProductOffer entry = new ProductOffer(product);
		space.write(entry, trans, 24*60*60);

		trans.commit();
	}
	
	public void addProduct(Product product, Long duration, Double minAmount, Double maxAmount, Class auctionType, CustomerInterface seller, UUID bankAccountID, BankInterface bank) throws RemoteException, TransactionException, LeaseDeniedException {
		this.getStore().addProduct(product);
		
		//Get JavaSpace and Transaction Manager
		JavaSpace space = this.getSpace();
		TransactionManager transManager = this.getTransactionManager();
		
		//Initialize transaction
		Created created = TransactionFactory.create(transManager, 5000);
		Transaction trans = created.transaction;
		
		//Create and write entry
		ProductOffer entry = new ProductOffer(product, duration, minAmount, maxAmount, auctionType, seller, bankAccountID, bank);
		space.write(entry, trans, 24*60*60);

		trans.commit();
	}
	
	public Vector<Product> search(String string) throws RemoteException{
		Vector<Product> matches = new Vector<Product>();
		for (int i = 0; i < fStores.size(); i++) {
			matches.addAll(fStores.get(i).search(string));
		}
		return matches;
	}

	public void discarded(DiscoveryEvent evt) {
		ServiceRegistrar[] registrars = evt.getRegistrars();
		for (int n = 0; n < registrars.length; n++) { 
			fRegistrars.remove(registrars[n]);
		}		
	}

	public void discovered(DiscoveryEvent evt) { 
		ServiceRegistrar[] registrars = evt.getRegistrars();
		for (int n = 0; n < registrars.length; n++) { 
			fRegistrars.add(registrars[n]);
		}
	}

	public void serviceAdded(ServiceDiscoveryEvent discoveryEvent) {
		fStores.add((StoreInterface) discoveryEvent.getPostEventServiceItem().service);
		System.out.println("[STORE CONNECTOR] Got store with ID " + discoveryEvent.getPostEventServiceItem().serviceID);
	}

	public void serviceChanged(ServiceDiscoveryEvent discoveryEvent) {

	}

	public void serviceRemoved(ServiceDiscoveryEvent discoveryEvent) {

	}
	
	public JavaSpace getSpace() throws RemoteException {
		Class[] classes = new Class[] {JavaSpace.class};
		JavaSpace javaSpace = null;

		for (int i = 0; i < fRegistrars.size(); i++) {
			JavaSpace tmp = (JavaSpace) fRegistrars.get(i).lookup(new ServiceTemplate(null, classes, null));
			if (tmp != null) {
				javaSpace = tmp;
			}
		}
		return javaSpace;
	}
	
	public TransactionManager getTransactionManager() throws RemoteException {
		TransactionManager transactionManager = null;
		
		Entry[] attributes = new Entry[1];
		attributes[0] = new Name ("Transaction Manager");
		Class[] classes = new Class[] {TransactionManager.class};
		
		for (int i = 0; i < fRegistrars.size(); i++) {			
			TransactionManager tmp = (TransactionManager) fRegistrars.get(i).lookup(new ServiceTemplate(null, classes, null));
			if (tmp != null) {
				transactionManager = tmp;
			}
		}
		
		if (transactionManager == null)
			System.out.println("[ERROR] - TransactionManager not found!");
		
		return transactionManager;
	}
}
